package nancal.mp.service.git.impl

import nancal.mp.db.mongo.entity.dev.GitServer
import nancal.mp.extend.toBjTime
import nancal.mp.model.git.GitBranchCommitModel
import nancal.mp.model.git.GitOrgItem
import nancal.mp.service.git.GitApiService
import nbcp.base.comm.ApiResult
import nbcp.base.comm.JsonMap
import nbcp.base.comm.ListResult
import nbcp.base.db.IdName
import nbcp.base.extend.*
import nbcp.base.model.HttpInvoke
import nbcp.base.utils.UrlUtil
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.nio.charset.StandardCharsets

/**
 * https://docs.gitlab.com/ee/api/#how-to-use-the-api
 *
 * 消除GitLab独有的子群组的概念。
 * group -> project 结构
 */
@Service
class GitLabService : GitApiService {
    companion object {
        private val logger = LoggerFactory.getLogger(this::class.java.declaringClass)
    }

    /**
     * 获取群组
     */
    override fun getOrgs(gitServer: GitServer): ListResult<IdName> {
        var url =
            "${gitServer.host}/api/v4/groups?private_token=${gitServer.accessToken}&all_available=yes&per_page=50&page="

        var page = 0;
        var list = mutableListOf<IdName>()
        while (true) {
            page++;
            var http = HttpInvoke()
            http.url = (url + page);
            http.requestMethod = "GET"
            var res = http.doInvoke();
            if (res.isError) {
                return ListResult.error("获取Git组织信息失败")
            }

            var items = res.responseText!!.FromListJson(JsonMap::class.java)
                //群组名在 name
                .map { IdName(it.get("id").AsString(), it.get("name").AsString()) }

            list.addAll(items)

            if (items.size < 50) {
                break;
            }
        }

        return ListResult.of(list)
    }


    /**
     *
     */
    override fun getRepos(gitServer: GitServer, groupId: String): ListResult<GitOrgItem> {
        var url =
            "${gitServer.host}/api/v4/groups/${groupId}/projects?private_token=${gitServer.accessToken}&per_page=50&page="

        var page = 0;
        var list = mutableListOf<GitOrgItem>()
        while (true) {
            page++;
            var http = HttpInvoke()
            http.url = (url + page);
            http.requestMethod = "GET"

            var res = http.doInvoke()
            if (res.isError) {
                return ListResult.error("获取Git组织信息失败")
            }

            var items = res.responseText!!.FromListJson(JsonMap::class.java)
                .map {
                    GitOrgItem(
                        it.get("id").AsString(),
                        it.get("path").AsString(),
                        it.get("name").AsString(),
                        it.get("http_url_to_repo").AsString(),
                        it.get("archived").AsBoolean()
                    )
                }

            list.addAll(items)
            if (items.size < 50) {
                break;
            }
        }

        return ListResult.of(list)
    }


    private fun getURIProjectId(
        group: String,
        repo: String
    ): String {
        //没错， 就是这样。
        return UrlUtil.encodeURIComponent("${group}/${repo}")
    }

    /**
     * 获取 分支信息
     */
    override fun getBranchCommitInfo(
        gitHost: String,
        accessToken: String,
        group: String,
        repo: String,
        branch: String
    ): ApiResult<GitBranchCommitModel> {


        var url =
            "${gitHost}/api/v4/projects/${
                getURIProjectId(
                    group,
                    repo
                )
            }/repository/branches/${branch}?private_token=${accessToken}&per_page=50&page="
        var http = HttpInvoke()
        http.url = (url);
        http.requestMethod = "GET"

        var res = http.doInvoke();
        if (res.isError) {
            throw RuntimeException("找不到Git分支信息")
        }

        var retMap = res.responseText!!.FromJson<JsonMap>().must().elseThrow { "找不到Git分支信息" };

        var commitMap = retMap.get("commit") as Map<String, *>
        var commitInfo = GitBranchCommitModel();

        commitInfo.commitId = commitMap.get("id").AsString()
        commitInfo.commitMessage = commitMap.get("message").AsString()
        commitInfo.committerName = commitMap.get("committer_name")
            .AsString({ commitMap.get("author_name").AsString() })
        commitInfo.commitAt = commitMap.get("committed_date").AsLocalDateTime()
        return ApiResult.of(commitInfo);
    }


    override fun getBranches(
        gitHost: String,
        accessToken: String,
        owner: String,
        repo: String,
        take: Int
    ): ListResult<GitBranchCommitModel> {
        var total = -1L;
        var page = 1;

        var list = mutableListOf<GitBranchCommitModel>()
        for (i in 0..9) {
            var ret = getPageBranches(gitHost, accessToken, owner, repo, page);
            if (ret.msg.HasValue) {
                return ret;

            }
            list.addAll(ret.data);
            total = ret.total;

            if (list.size >= total) {
                break;
            }

            page++;
        }

        list.sortByDescending { it.commitAt }

        list.forEach {
            it.commitAt = it.commitAt?.toBjTime()
        }

        return ListResult.of(list.Slice(0, if (take <= 0) 36 else take), total);
    }

    override fun existsFile(
        gitHost: String,
        accessToken: String,
        owner: String,
        repo: String,
        branchName: String,
        path: String
    ): ApiResult<Boolean> {
        TODO("Not yet implemented")
    }

    private fun getPageBranches(
        gitHost: String,
        accessToken: String,
        group: String,
        repo: String,
        page: Int
    ): ListResult<GitBranchCommitModel> {
        var url =
            "${gitHost}/api/v4/projects/${
                getURIProjectId(
                    group,
                    repo
                )
            }/repository/branches?private_token=${accessToken}&per_page=50&page=" + page


        var http = HttpInvoke()
        http.url = (url);
        http.requestMethod = "GET"
        var res = http.doInvoke();
        if (res.isError) {
            if (res.status == 401) {
                return ListResult.error("Git token 非法", res.status)
            }
            return ListResult.error("Git请求错误", res.status)
        }

        var gitFileData = res.responseText!!

        var list = gitFileData.FromListJson(JsonMap::class.java).map { branch ->
            var item = GitBranchCommitModel();
            item.name = branch.get("name").AsString()
            val commitJson = branch.get("commit") as Map<String, *>;
            item.commitId = commitJson.get("id").AsString()
            item.commitMessage = commitJson.get("message").AsString().trim();
            item.committerName = commitJson.get("committer_name").AsString(commitJson.get("author_name").AsString());
            item.commitAt = commitJson.get("committed_date").AsString().AsLocalDateTime() //"2012-06-28T03:44:20-07:00"
            return@map item;
        }

        return ListResult.of(list, res.responseHeader.get("x-total-count").AsLong())
    }


    class ProjectSearchResult {
        class NamespaceModel {
            var id: Int? = 0
            var name: String? = ""
            var path: String? = ""
            var kind: String? = ""
            var full_path: String? = ""
            var parent_id: Any? = null
            var avatar_url: Any? = null
            var web_url: String? = ""
        }

        var id: Int? = 0
        var description: Any? = null
        var name: String? = ""
        var name_with_namespace: String? = ""
        var path: String? = ""
        var path_with_namespace: String? = ""
        var created_at: String? = ""
        var default_branch: String? = ""
        var tag_list: List<String>? = listOf<String>()
        var topics: List<String>? = listOf<String>()
        var ssh_url_to_repo: String? = ""
        var http_url_to_repo: String? = ""
        var web_url: String? = ""
        var readme_url: String? = ""
        var avatar_url: Any? = null
        var forks_count: Int? = 0
        var star_count: Int? = 0
        var last_activity_at: String? = ""
        var namespace: NamespaceModel? = NamespaceModel()
    }

    private fun searchProjectId(
        gitHost: String,
        accessToken: String,
        group: String,
        repo: String
    ): ApiResult<String> {
        var url = "${gitHost}/api/v4/search?private_token=${accessToken}&scope=projects&search=${repo}"

        var http = HttpInvoke()
        http.url = (url);
        http.requestMethod = "GET"

        var res = http.doInvoke();
        if (res.isError) {
            return ApiResult.error(res.responseText.AsString("error"));
        }

        var finded = res.responseText!!.FromListJson(ProjectSearchResult::class.java)
            .firstOrNull { it.namespace!!.path == group }
        if (finded == null) {
            return ApiResult.error("找不到 ${group}.${repo}")
        }

        return ApiResult.of(finded.id.AsString());
    }

    override fun getRawFile(
        gitHost: String,
        accessToken: String,
        group: String,
        repo: String,
        branch: String,
        fileName: String
    ): ApiResult<ByteArray> {

        var projectResult = searchProjectId(gitHost, accessToken, group, repo);
        if (projectResult.hasError) {
            return ApiResult.error(projectResult.msg)
        }

        var url =
            "${gitHost}/api/v4/projects/${projectResult.data!!}/repository/files/${UrlUtil.encodeURIComponent(fileName)}/raw?private_token=${accessToken}&ref=${branch}"


        var http = HttpInvoke()
        http.url = (url);
        http.requestMethod = "GET"
        var res = http.doInvoke();


        if (res.isError) {
            return ApiResult.error("Git请求错误", res.status)
        }

        return ApiResult.of(res.responseText?.toByteArray(StandardCharsets.UTF_8))
    }

}
